using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using com.youzu.got.protocol;
using UnityEngine;
using WorldMapNew;
using XLua;
using Yoozoo.Framework.Core;
using Yoozoo.Managers;
using UMT.Framework.Managers;
using UnityEngine.Profiling;
using Yoozoo.Core.Common;
using Yoozoo.Managers.ResourceManagerV2.Runtime;
using Recorder = Yoozoo.Managers.GameScene.Runtime.Record.Recorder;

namespace WorldMapBase.Tile.Pool
{
    /// <summary>
    /// 沙盘静态层的对象池管理器
    /// </summary>
    public class WorldPoolManager: SingletonMono<WorldPoolManager>
    {
#if UNITY_EDITOR
        private static Dictionary<int,int> resId2MaxNum = new Dictionary<int, int>();
        private static Dictionary<int,int> resId2CurNum = new Dictionary<int, int>();
        private static Dictionary<int,string> resId2Name = new Dictionary<int, string>();
#endif

        public static Transform InstanceRoot;
        public static Transform TreeRoot;

        public static Transform GetTreeRoot()
        {
            return TreeRoot;
        }

        public static Transform TerrainRoot;

        public static Transform GetTerrainRoot()
        {
            return TerrainRoot;
        }

        public static Transform GetInstanceRoot()
        {
            return InstanceRoot;
        }

        private static Dictionary<int, WorldPrefabPool> _worldPrefabPools;
        public static void Init()
        {
            ClassPoolManager.Init();

            WorldPoolManager.GetInstance("WorldPoolManager").OnInit();
            WorldPoolManager.GetInstance("WorldPoolManager").Initialize();
        }

        private static ResLoader loader;
        private static Dictionary<string, GameObject> preloadAssets = new Dictionary<string, GameObject>();
        private static Dictionary<int, ResLoader> assetLoaders = new Dictionary<int, ResLoader>();
        private static Dictionary<int, List<CommandTileElement>> waitLoadElements = new Dictionary<int, List<CommandTileElement>>();

        public static void PreloadAssets(LuaFunction onComplete)
        {
            LoadingUtils.RecodeTimeStart("WorldPoolManager.PreloadAssets");
            var resArray = WorldMapConfigData.ResourceDatas;
            loader = ResLoader.Alloc();

            foreach (var data in resArray.Values)
            {
                var path = data.resPath;
                if (data.loadLevel == (int)ResourceLoadLevel.E_Load_ByLoading || path.Contains("tiles"))
                {
                    loader.Add2Load(path, (success, name, obj) => { preloadAssets[path] = obj as GameObject; });
                }
            }
            loader.Load(() =>
            {
                Init();
                onComplete?.Action(0);
            });
            LoadingUtils.RecodeTimeEnd("WorldPoolManager.PreloadAssets");
        }
        
        public void OnInit()
        {
            // Dictionary<int,int> resNum = new Dictionary<int, int>()
            // {
            // };

            // Lod0树木资源ID
            // HashSet<int> lod0TreeResIds = new HashSet<int>()
            // {
            // };

            this.transform.SetParent(PoolCollectionManager.GetInstance("PoolCollectionManager").transform);
            
            InstanceRoot = (new GameObject("InstanceRoot")).transform;
            TreeRoot = (new GameObject("Tree")).transform;
            TerrainRoot = (new GameObject("Terrain")).transform;

            InstanceRoot.SetParent(this.transform);
            TreeRoot.SetParent(this.transform);
            TerrainRoot.SetParent(this.transform);

            Dictionary<int, ResourceData> resourceMap = WorldMapConfigData.ResourceDatas;
            _worldPrefabPools = new Dictionary<int, WorldPrefabPool>(resourceMap.Keys.Count);
     
            foreach (var v in resourceMap.Values)
            {
                //recorder.GetData(v.resPath);
                if (!preloadAssets.TryGetValue(v.resPath,out var tmp))
                {
                    continue;
                }

                int initNum = 1;
                // if (resNum.ContainsKey(v.resID))
                // {
                //     //initNum = resNum[v.resID];
                // }

                WorldPrefabPool prefabPool = null;
                // if (lod0TreeResIds.Contains(v.resID))
                // {
                //     prefabPool = new ScaleTreePrefabPool(1,v.poolNum,v.resPath);
                // }
                // else
                // {
                    prefabPool  = new WorldPrefabPool(1,v.poolNum,v.resPath);
                // }
                _worldPrefabPools[v.resID] = prefabPool;
                prefabPool.PreLoad(tmp, v.resPath);
            }
#if UNITY_EDITOR
            //这里将资源ID和名字关联一下；用于输出统计数据，
            foreach (var v in resourceMap.Values)
            {
                if(!resId2Name.ContainsKey(v.resID))
                {
                    Regex regex = new Regex(@"Assets\/ResourcesAssets\/World\/prefabs\/sandtable\/.*\/[A-Za-z]+_(.*)\.prefab");
                    var match = regex.Match(v.resPath);
                    var group = match.Groups[1];
                    var name = group.ToString();
                    resId2Name.Add(v.resID,name);
                };

            }
#endif
            
        }

        public static void AddOnePool(int resID)
        {
            if (assetLoaders.ContainsKey(resID)) return;

            Dictionary<int, ResourceData> resourceMap = WorldMapConfigData.ResourceDatas;
            if (resourceMap.TryGetValue(resID,out ResourceData data))
            {
                ResLoader resLoader = ResLoader.Alloc();
                assetLoaders[resID] = resLoader;
                var path = data.resPath;
                resLoader.Add2Load(path, (success, name, obj) => {
                    var objAssets = obj as GameObject;
                    preloadAssets[path] = objAssets;
                    InitOnePool(resID, objAssets);
                });
                resLoader.Load();
            }
        }

        public static void InitOnePool(int resID, GameObject obj)
        {
            Dictionary<int, ResourceData> resourceMap = WorldMapConfigData.ResourceDatas;
            if (resourceMap.TryGetValue(resID, out ResourceData data))
            {
                WorldPrefabPool prefabPool = new WorldPrefabPool(1, data.poolNum, data.resPath);
                // }
                _worldPrefabPools[data.resID] = prefabPool;
                prefabPool.PreLoad(obj, data.resPath);
                LoadResCallBack(resID);
            }
  
        }

        public static WorldObject GetWorldObject(int lodIndex, int mapLayer, int resID)
        {
#if UNITY_EDITOR
            if (resId2CurNum.ContainsKey(resID))
            {
                resId2CurNum[resID] = resId2CurNum[resID] + 1;
            }
            else
            {
                resId2CurNum[resID] = 1;
            }

            if (resId2MaxNum.ContainsKey(resID))
            {
                if (resId2CurNum[resID] > resId2MaxNum[resID])
                {
                    resId2MaxNum[resID] = resId2CurNum[resID];
                }
            }
            else
            {
                resId2MaxNum[resID] = resId2CurNum[resID];
            }
#endif
            WorldPrefabPool prefabPool = null;
            _worldPrefabPools.TryGetValue(resID, out prefabPool);
            if (prefabPool != null)
            {
                WorldObject obj = null;
                bool bAddTf = true;
                if (mapLayer == WorldEntry.TREE_LAYER_ID)
                {
                    obj = ClassPoolManager.GetTreeObject();
                }
                else if (mapLayer == WorldEntry.ALLIANCEPOINT_CROSS_LAYER_ID || mapLayer == WorldEntry.ALLIANCEPOINT_ROAD_LAYER_ID || mapLayer == WorldEntry.MAP_POINT_ADORN_LAYER_ID)
                {
                    obj = ClassPoolManager.GetAdornObject();
                }
                else
                {
                    obj = ClassPoolManager.GetWorldObject();
                }

                Transform tf = prefabPool.GetItem();
                obj.OnCreate(lodIndex, resID, tf);
                return obj;
            }
            else
            {
                AddOnePool(resID);
                return null;
            }
        }

        public static void RecoverItem(WorldObject worldObject)
        {
            if (worldObject == null)
            {
                return;
            }

#if UNITY_EDITOR
            if (resId2CurNum.ContainsKey(worldObject.ResID))
            {
                resId2CurNum[worldObject.ResID] = resId2CurNum[worldObject.ResID] - 1;
            }
            else
            {
                resId2CurNum[worldObject.ResID] = 0;
            }
#endif
            if (worldObject.GetRefTrans != null)
            {
                WorldPrefabPool prefabPool = null;
                _worldPrefabPools.TryGetValue(worldObject.ResID, out prefabPool);
                if (prefabPool != null)
                {
                    prefabPool.RecoverItem(worldObject.GetRefTrans);
                }
                worldObject.OnRecovered();
            }
            //ClassManager.Free(worldObject);
        }


        public static void DestroyPool()
        {

            if (WorldPoolManager.GetInstance().IsInitialized())
            {
                ClassPoolManager.Destory();
                
                foreach (var worldPre in _worldPrefabPools)
                {
                    worldPre.Value.Destroy();
                }
                _worldPrefabPools.Clear();
                
                if (InstanceRoot)
                {
                    Destroy(InstanceRoot.gameObject);
                    InstanceRoot = null;
                }
            
                if (TreeRoot)
                {
                    Destroy(TreeRoot.gameObject);
                    TreeRoot = null;
                }
            
                if (TerrainRoot)
                {
            
                    Destroy(TerrainRoot.gameObject);
                    TerrainRoot = null;
                }
                GetInstance().Dispose();
                
                loader.Recycle2Cache();
                foreach (var resLoader in assetLoaders.Values)
                {
                    resLoader.Recycle2Cache();
                }

                assetLoaders.Clear();
                preloadAssets.Clear();
            }
        }

        public override void Dispose()
        {
            base.Dispose();
        }

#if UNITY_EDITOR

        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.F9))
            {
                DebugMaxNum();
            }
            
        }
#endif

#if UNITY_EDITOR
        private void DebugMaxNum()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("[沙盘物体数量][静态物体]：");
            foreach (var m in resId2MaxNum)
            {
                sb.AppendLine(string.Format($"{resId2Name[m.Key],-50}currentNum: {resId2CurNum[m.Key],-10}maxNum: {m.Value,-5}"));
            }

            UnityEngine.Debug.Log(sb.ToString());
        }
#endif

        public static void UpdateMat(int lodLayer)
        {
    
        }

        public static void RegisterLoadCallBack(int resId, CommandTileElement element)
        {
            if (!waitLoadElements.ContainsKey(resId))
            {
                waitLoadElements[resId] = new List<CommandTileElement>();
            }
            waitLoadElements[resId].Add(element);
        }

        public static void LoadResCallBack(int resId)
        {
            if (waitLoadElements.TryGetValue(resId, out List<CommandTileElement> list))
            {
                for (int i = 0; i < list.Count; i++)
                {
                    CommandTileElement element = list[i];
                    element.LoadResCallBack(resId);
                }
                waitLoadElements.Remove(resId);
            }
        }

        public static void UnregisterLoadCallBack(int resId, CommandTileElement element)
        {
            if (waitLoadElements.TryGetValue(resId,out List<CommandTileElement> list))
            {
                list.Remove(element);
            }
        }
    }
}
